﻿using System;
using System.Collections.Generic;
using System.Text;
using Vortex.Drawing;
using Vortex.Input;
using Vortex.SceneFramework;

namespace Vortex.Demo.Pacman {

	/// <summary>
	/// Defines pacman unit element
	/// </summary>
	class PacmanUnit : Unit {
		public const float MOUTH_OPENED_ANGLE = 60.0f / 180.0f * (float)Math.PI;

		///<summary>Last direction was choosen by user from keyboard</summary>
		private Vector2 _userDirection;

		///<summary>Current direction of pacman pie</summary>
		private float _directionAngle;
		///<summary>Contains phase of Pacman mouth</summary>
		private float _mouthPhase;
		///<summary>Direction of mouse phase; <c>true</c> if mouse is opening; <c>false</c> if closing.</summary>
		private bool _mouthPhaseDirection;

		private SimpleSpriteAnimation DyingPacmanAnimation;

		[ElementRef]
		private TextElement DebugInfoText = null;

		[ResourceRef]
		private Sprite PacmanPie = null;

		#region Properties

		/// <summary>
		/// Gets or sets the score of pacman.
		/// </summary>
		/// <value>The score of pacman.</value>
		public int Score { get; set; }

		/// <summary>
		/// Gets or sets the life count of Pacman.
		/// </summary>
		/// <value>The life count of Pacman.</value>
		public int Lifes { get; set; }

		#endregion

		#region Events

		/// <summary>
		/// Occurs when generic point has been eated.
		/// </summary>
		public event CallbackHandler PointEated;

		/// <summary>
		/// Occurs when big pulsing point has been eated.
		/// </summary>
		public event CallbackHandler BigPointEated;

		#endregion

		public override void ComponentLoad(Vortex.Resources.IResourceCollection resources) {
			base.ComponentLoad(resources);
			
			//create simple animation for dying
			DyingPacmanAnimation = new SimpleSpriteAnimation(resources.Get<ISpriteCollection>("DyingPacmanGrid"), 16);
		}

		public override void Update(float timeDelta) {
			//Pacman processing can be done only if world and pacman enabled 
			if (World.IsEnabled && IsEnabled) {
				ReadUserDirection();
				ProcessTurn();
				ProcessMovement(timeDelta);
				CheckWorldBounds();
				UpdatePacmanAngle();

			}

			//update animation
			DyingPacmanAnimation.Update(timeDelta);

			DebugInfoText.Text = string.Format("Location: {0}, Direction: {1}", Location, Direction);
		}

		private void UpdatePacmanAngle() {
			if (!Direction.IsEmpty) {
				_directionAngle = (float)Math.Atan2(Direction.Y, Direction.X);
			}
		}

		/// <summary>
		/// Processes the direction turn.
		/// </summary>
		private void ProcessTurn() {
			if (!_userDirection.IsEmpty) {
				bool turn = !_userDirection.CollinearTo(Direction);
				if (turn) {
					Vector2 cellLocation = Location.Rounded;
					if (Location.Distance(cellLocation) < TURN_THRESOLD) {
						if (CanGoTo(cellLocation + _userDirection)) {
							Location = cellLocation;
							Direction = _userDirection;
						}
					}
				} else {
					if (CanGoTo(Location + _userDirection)) {
						Direction = _userDirection;
					}
				}				
			}
		}

		/// <summary>
		/// Processes the movement of unit.
		/// </summary>
		/// <param name="timeDelta">The time delta.</param>
		private void ProcessMovement(float timeDelta) {
			if (!Direction.IsEmpty) {
				Vector2 newLocation = Location + Direction * Speed * timeDelta;
				//check to stop if some wall is reached
				if (!CanGoTo(newLocation + Direction.Half)) {
					Stop();
				} else {
					Location = newLocation;
					ProcessMouthPhase(timeDelta);
					CheckEatPoint();
				}
			}
		}

		/// <summary>
		/// Checks the eat of any point.
		/// </summary>
		private void CheckEatPoint() {
			int x = (int)Math.Round(Location.X), y = (int)Math.Round(Location.Y);
			DotType type = World.Map.GrabCell(x, y);
			switch (type) {
				case DotType.Small:
					Score += Constants.SMALL_POINT_SCORE;
					if (null != PointEated) PointEated();
					break;
				case DotType.Big:
					Score += Constants.BIG_POINT_SCORE;
					if (null != BigPointEated) BigPointEated();
					break;
			}
		}

		/// <summary>
		/// Processes the mouth phase animation.
		/// </summary>
		/// <param name="timeDelta">The time delta.</param>
		private void ProcessMouthPhase(float timeDelta) {
			if (_mouthPhaseDirection) {
				_mouthPhase += timeDelta * Speed * 1.5f;
			} else {
				_mouthPhase -= timeDelta * Speed * 1.5f;
			}

			if (_mouthPhase < 0 || _mouthPhase > 1) {
				_mouthPhaseDirection = !_mouthPhaseDirection;
				_mouthPhase = Math.Min(Math.Max(_mouthPhase, 0), 1);
			}
		}

		private void ReadUserDirection() {
			if (Game.Keyboard.IsDown(Key.Down)) {
				_userDirection = Vector2.Down;
			} else if (Game.Keyboard.IsDown(Key.Up)) {
				_userDirection = Vector2.Up;
			} else if (Game.Keyboard.IsDown(Key.Left)) {
				_userDirection = Vector2.Left;
			} else if (Game.Keyboard.IsDown(Key.Right)) {
				_userDirection = Vector2.Right;
			}
		}

		/// <summary>
		/// Draws the pacman unit
		/// </summary>
		/// <param name="canvas">The canvas to use for element drawing.</param>
		protected override void Draw(Canvas2D canvas) {
			if (IsEnabled) {
				canvas.DrawSprite(Vector2.Zero, Vector2.Unit * 2, _directionAngle - MOUTH_OPENED_ANGLE * _mouthPhase, PacmanPie, ColorU.Blank);
				canvas.DrawSprite(Vector2.Zero, Vector2.Unit * 2, _directionAngle + MOUTH_OPENED_ANGLE * _mouthPhase + MathHelper.Pi, PacmanPie, ColorU.Blank);
			} else {
				canvas.DrawSprite(Vector2.Zero, Vector2.Unit * 2, -MathHelper.PiOver2, DyingPacmanAnimation.ToSprite(), ColorU.Yellow);
			}
		}

		#region World Events

		///<summary>Event is occured when life is lost</summary>
		public event LostLifeCallbackHandler LostLife;

		#endregion

		/// <summary>
		/// Kills Pacman unit.
		/// </summary>
		public void Kill() {
			Lifes = Lifes - 1;
			IsEnabled = false;

			DyingPacmanAnimation.Replay();

			if (LostLife != null) {
				LostLife(Lifes);
			}
		}

		/// <summary>
		/// Initializes the pacman with specified location and direction.
		/// </summary>
		/// <param name="point">The start point.</param>
		/// <param name="direction">The direction of pacman.</param>
		public void Initialize(Vector2 point, Vector2 direction) {
			IsVisible = true;
			IsEnabled = true;
			Location = point;
			Direction = direction;			
			UpdatePacmanAngle();

			_userDirection = Vector2.Zero;
			_mouthPhase = 0.5f;
			_mouthPhaseDirection = true;
		}
	}
}
